﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using System.Net;
using System.IO;

namespace OviNotificationsApi
{
    public class RestBase
    {

        #region Private members
        /// <summary>
        /// Enum of Endpoints, used to index the endpoint dictionary
        /// </summary>
        protected enum EndpointEnum
        {
            /// <summary>
            /// [POST] "/{version}/jid/{jid}". Send notifications where {jid}: Client application user ID. For example: ellen.mckelly@ovi.com
            /// </summary>
            SendNotificationJid,
            /// <summary>
            /// [POST] "/{version}/nid/{nid}". Send notifications where {nid}: Notification ID; must be url encoded.
            /// </summary>
            SendNotificationNid,
            /// <summary>
            /// [GET] "/version". Returns the current version of the Notifications API.
            /// </summary>
            Version,
            /// <summary>
            /// [GET] "/{version}/ping". Use this method to ensure that the network connection to the Notification Server is alive and that your service credentials are valid.
            /// </summary>
            PingService,
            /// <summary>
            /// [GET] "/{version}/ping/jid/{jid}/{appid}". Use this method to check if a client application is currently capable of receiving notifications. Note that a successful client ping operation does not guarantee that a notification will be delivered to the client application.
            /// </summary>
            PingClientJidAppId,
            /// <summary>
            /// [GET] "/{version}/ping/nid/{nid}". Use this method to check if a client application is currently capable of receiving notifications. Note that a successful client ping operation does not guarantee that a notification will be delivered to the client application.
            /// </summary>
            PingClientNid,
            /// <summary>
            /// [GET] "/{version}/limits". Get query limits.
            /// </summary>
            QueryLimits
        }

        /// <summary>
        /// Dictionary of EndPoints
        /// </summary>
        protected static Dictionary<EndpointEnum, string> EndPoints = new Dictionary<EndpointEnum, string> {
            {EndpointEnum.SendNotificationJid,"/nnapi/{0}/jid/{1}"},
            {EndpointEnum.SendNotificationNid,"/nnapi/{0}/nid/{1}"},
            {EndpointEnum.QueryLimits,"/nnapi/{0}/limits"},
            {EndpointEnum.PingService,"/nnapi/{0}/ping"},
            {EndpointEnum.PingClientJidAppId,"/nnapi/{0}/ping/jid/{1}/{2}"},
            {EndpointEnum.PingClientNid,"/nnapi/{0}/ping/nid/{1}"},
            {EndpointEnum.Version,"/nnapi/version"}
        };
        #endregion

        #region Properties
        protected string _baseUrl;

        /// <summary>
        /// Gets or sets the base URL of the service.
        /// </summary>
        /// <value>The base URL.</value>
        public string BaseUrl
        {
            get { return _baseUrl; }
            set { _baseUrl = value; }
        }

        protected string _serviceID;

        /// <summary>
        /// Gets or sets the service ID.
        /// </summary>
        /// <value>The service ID.</value>
        public string ServiceID
        {
            get { return _serviceID; }
            set { _serviceID = value; }
        }

        protected string _serviceSecret;

        /// <summary>
        /// Gets or sets the service secret.
        /// </summary>
        /// <value>The service secret.</value>
        public string ServiceSecret
        {
            get { return _serviceSecret; }
            set { _serviceSecret = value; }
        }

        protected string _apiVersion;

        /// <summary>
        /// Gets or sets the API version.
        /// </summary>
        /// <value>The API version.</value>
        public string ApiVersion
        {
            get { return _apiVersion; }
            set { _apiVersion = value; }
        }

        #endregion
        /// <summary>
        /// Setups the request.
        /// </summary>
        /// <param name="finalEndpoint">The final endpoint.</param>
        /// <param name="type">The type.</param>
        /// <returns></returns>
        protected HttpWebRequest SetupRequest(string finalEndpoint, string type)
        {
            // Create the web request  
            HttpWebRequest request = WebRequest.Create(new Uri(finalEndpoint)) as HttpWebRequest;

            // Set request type 
            request.Method = type;
            if (type == WebRequestMethods.Http.Post)
                request.ContentType = "application/x-www-form-urlencoded";

            Console.WriteLine(type + " (anonymous): " + finalEndpoint);

            return request;
        }

        /// <summary>
        /// Setups the request.
        /// </summary>
        /// <param name="finalEndpoint">The final endpoint.</param>
        /// <param name="type">The type.</param>
        /// <param name="username">The username.</param>
        /// <param name="password">The password.</param>
        /// <returns></returns>
        protected HttpWebRequest SetupRequest(string finalEndpoint, string type, string username, string password)
        {
            // Create the web request  
            HttpWebRequest request = WebRequest.Create(finalEndpoint) as HttpWebRequest;

            // Set request type 
            request.Method = type;
            if (type == WebRequestMethods.Http.Post)
                request.ContentType = "application/x-www-form-urlencoded";

            // Add authentication to request  
            request.Credentials = new NetworkCredential(username, password);

            Console.WriteLine(type + " (authenticated): " + finalEndpoint);
            return request;
        }

        /// <summary>
        /// Adds the parameter and URL encodes its value using the default encoding (UTF8).
        /// </summary>
        /// <param name="request">The request.</param>
        /// <param name="key">The key.</param>
        /// <param name="value">The value.</param>
        protected void AddPostParameter(Dictionary<string, string> parameters, string key, string value)
        {
            AddPostParameter(parameters, key, value, Encoding.UTF8);
        }

        /// <summary>
        /// Adds the parameter and URL encodes its value.
        /// </summary>
        /// <param name="request">The request.</param>
        /// <param name="key">The key.</param>
        /// <param name="value">The value.</param>
        /// <param name="encoding">The encoding.</param>
        protected void AddPostParameter(Dictionary<string, string> parameters, string key, string value, Encoding encoding)
        {
            parameters.Add(key, HttpUtility.UrlEncode(value, encoding));
        }

        /// <summary>
        /// Returns the parameters as a string in the form key1=val1&key2=val2
        /// </summary>
        /// <param name="parameters">The parameters.</param>
        /// <returns></returns>
        protected string PostParametersToString(Dictionary<string, string> parameters)
        {
            StringBuilder data = new StringBuilder();
            int count = 0;
            foreach (KeyValuePair<string, string> parameter in parameters)
            {
                // If the count>0, prepend "&"
                data.AppendFormat("{0}{1}={2}", count > 0 ? "&" : "", parameter.Key, parameter.Value);
                count++;
            }
            return data.ToString();
        }

        /// <summary>
        /// Returns the parameters as a byte[]
        /// </summary>
        /// <param name="parameters">The parameters.</param>
        /// <returns></returns>
        protected byte[] PostParametersToByteArray(Dictionary<string, string> parameters)
        {
            // Create a byte array of the data we want to send  
            return UTF8Encoding.UTF8.GetBytes(PostParametersToString(parameters));
        }

        /// <summary>
        /// Appends the parameters to the request.
        /// </summary>
        /// <param name="request">The request.</param>
        /// <param name="parameters">The parameters.</param>
        protected void AppendPostParametersToRequest(HttpWebRequest request, Dictionary<string, string> parameters)
        {
            // Create a byte array of the data we want to send  
            byte[] byteData = this.PostParametersToByteArray(parameters);

            // Set the content length in the request headers  
            request.ContentLength = byteData.Length;

            // Write data  
            using (Stream postStream = request.GetRequestStream())
            {
                postStream.Write(byteData, 0, byteData.Length);
            }
        }
    }
}
